//! @file   lc_system.cpp
//! @brief  
//! @author Sayan Chaliha
//! @date   September, 2011
//!
//! 
#include <lc_system.h>
#include <lc_system_exception.h>
#include <lc_basic_string.h>
#include <lc_interval.h>

namespace cyan {    
  void System::throwSystemException(Int errorNumber) {
#ifdef LC_EXCEPTIONS
    throw SystemException(errorNumber);
#else
    UNREFERENCED_PARAMETER(errorNumber);
    ::std::abort();
#endif /* LC_EXCEPTIONS */
  }
    
  Size System::standardBufferSize() {
#ifdef BUFSIZ
    return BUFSIZ;
#else
    return 1024;
#endif /* BUFSIZ */
  }
    
  Size System::maximumFileNameLength() {
#ifdef FILENAME_MAX
    return FILENAME_MAX;
#else
    return 1024;
#endif /* FILENAME_MAX */
  }
    
  Size System::maximumPathLength() {
#if MAXPATHLEN
    return MAXPATHLEN;
#elif defined (PATH_MAX)
    return PATH_MAX;
#else
    return 1024;
#endif /* MAXPATHLEN */
  }
    
  void System::sleep(UInt seconds) {
#ifdef LC_OS_UNIX_LIKE
    ::sleep(seconds);
#elif defined (LC_OS_WINDOWS)
    ::Sleep(seconds * 1000);
#endif /* LC_OS_UNIX_LIKE */
  }
    
  String System::errorMessage(Int errorCode) {
    String errorMessage;
    Char* buffer = new Char[System::standardBufferSize()];
#ifdef HAVE_DECL_STRERROR_R
# ifdef STRERROR_R_CHAR_P
    errorMessage.assign(::strerror_r(errorCode, buffer,
        System::standardBufferSize()));
# else
    ::strerror_r(errorCode, buffer, System::standardBufferSize());
# endif /* STRERROR_R_CHAR_P */
#elif defined (HAVE_STRERROR_S)
    ::strerror_s(buffer, System::maximumBufferSize(),
        errorCode);
    errorMessage.assign(buffer);
#endif /* HAVE_DECL_STRERROR_R */
    delete [] buffer;
        
    return errorMessage;
  }
    
  Size System::pageSize() {
    Size returnValue = 4096;
#ifdef HAVE_SYS_SYSCTL_H
# ifdef LC_OS_DARWIN
    static Int mib[] = {CTL_HW, HW_PAGESIZE};
    static const Size mibLength = sizeof (mib) / sizeof (Int);
    Size returnValueSize = sizeof (returnValue);
        
    if (::sysctl(mib, mibLength, &returnValue,
        &returnValueSize, nullptr, 0) != 0)
      System::throwSystemException();
# endif /* LC_OS_DARWIN */
#endif /* HAVE_SYS_SYSCTL_H */
    return returnValue;
  }
    
  Size System::physicalMemory() {
    Size returnValue = 0;
#ifdef HAVE_SYS_SYSCTL_H
# ifdef LC_OS_DARWIN
    static Int mib[] = {CTL_HW, HW_MEMSIZE};
    static const Size mibLength = sizeof (mib) / sizeof (Int);
    Size returnValueSize = sizeof (returnValue);
        
    if (::sysctl(mib, mibLength, &returnValue,
        &returnValueSize, nullptr, 0) != 0)
      System::throwSystemException();
# endif /* LC_OS_DARWIN */
#endif /* HAVE_SYS_SYSCTL_H */
    return returnValue;
  }
    
  String System::machine() {
    String machine;
#ifdef HAVE_SYS_SYSCTL_H
# ifdef LC_OS_DARWIN
    static Int mib[] = {CTL_HW, HW_MACHINE};
    static const Size mibLength = sizeof (mib) / sizeof (Int);
    Size length = System::standardBufferSize();
    Char* buffer = new Char[length];
        
    if (::sysctl(mib, mibLength, buffer, &length, nullptr, 0) != 0)
      System::throwSystemException();
        
    machine.assign(buffer, length);
    delete [] buffer;
# endif /* LC_OS_DARWIN */
#endif /* HAVE_SYS_SYSCTL_H */
    return machine;
  }
    
  String System::model() {
    String model;
#ifdef HAVE_SYS_SYSCTL_H
# ifdef LC_OS_DARWIN
    static Int mib[] = {CTL_HW, HW_MODEL};
    static const Size mibLength = sizeof (mib) / sizeof (Int);
    Size length = System::standardBufferSize();
    Char* buffer = new Char[length];
    
    if (::sysctl(mib, mibLength, buffer, &length, nullptr, 0) != 0)
      System::throwSystemException();
        
    model.assign(buffer, length);
    delete [] buffer;
# endif /* LC_OS_DARWIN */
#endif /* HAVE_SYS_SYSCTL_H */
    return model;
  }
    
  Time System::bootTime() {
#ifdef HAVE_SYS_SYSCTL_H
# ifdef LC_OS_DARWIN
    static Int mib[] = {CTL_KERN, KERN_BOOTTIME};
    static const Size mibLength = sizeof (mib) / sizeof (Int);
    ::timeval time;
    Size timeLength = sizeof (::timeval);
    
    if (::sysctl(mib, mibLength, &time, &timeLength, nullptr, 0) != 0)
      System::throwSystemException();
    return time.tv_sec + (time.tv_usec / Interval::MICROSECONDS_PER_SECOND);
# endif /* LC_OS_DARWIN */
#endif /* HAVE_SYS_SYSCTL_H */
    return 0;
  }
    
  UInt System::ticksPerSecond() {
#ifdef HAVE_SYSCONF
    return UInt(::sysconf(_SC_CLK_TCK));
#else
    return 1000000;
#endif /* CLOCKS_PER_SECOND */
  }
    
  String System::environmentVariable(const String& name) {
    Char* value = nullptr;
#ifdef HAVE_GETENV
    value = ::getenv(name);
#endif /* HAVE_GETENV */
    return String(value ? value : "");
  }
    
  void System::environmentVariable(const String& name, const String& value) {
#ifdef HAVE_SETENV
    ::setenv(name, value, 1);
#elif HAVE__PUTENV_S
    ::_putenv_s(name, value);
#endif /* HAVE_SETENV */
  }
}
